/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.ibatis.extension.parse;

import com.je.ibatis.extension.metadata.IdType;
import com.je.ibatis.extension.metadata.model.Column;
import com.je.ibatis.extension.metadata.model.Function;
import com.je.ibatis.extension.metadata.model.Id;
import com.je.ibatis.extension.metadata.model.Table;
import com.je.ibatis.extension.toolkit.DateUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.Timestamp;
import java.util.*;

/**
 * 元数据解析默认实现
 *
 * @author wangmm@ketr.com.cn
 * @date 2019/11/22
 */
public class DefaultMetaDataParse implements MetaDataParse {

    protected static final String TABLES;
    protected static final String TABLES_IN;
    protected static final String KEYS;
    protected static final String COLUMNS;
    protected static final String FUNCTIONS;
    protected static final String FUNCTIONS_IN;
    protected static final String FUNCTION_LOAD_COLUMNS;
    private static final String[] NUMBER_FIELD_TYPE = {"NUMBER", "FLOAT2", "FLOAT"};

    static {
        TABLES = "select RESOURCETABLE_KEY_GENERATOR_TYPE,RESOURCETABLE_KEY_GENERATOR_SQL,RESOURCETABLE_INCREMENTER_NAME,JE_CORE_RESOURCETABLE_ID,RESOURCETABLE_TABLENAME,RESOURCETABLE_TABLECODE,RESOURCETABLE_PKCODE from je_core_resourcetable where SY_DISABLED='0'";
        TABLES_IN = "select RESOURCETABLE_KEY_GENERATOR_TYPE,RESOURCETABLE_KEY_GENERATOR_SQL,RESOURCETABLE_INCREMENTER_NAME,JE_CORE_RESOURCETABLE_ID,RESOURCETABLE_TABLENAME,RESOURCETABLE_TABLECODE,RESOURCETABLE_PKCODE from je_core_resourcetable where SY_DISABLED='0' AND RESOURCETABLE_TABLECODE =?";
        KEYS = "select TABLEKEY_COLUMNCODE,TABLEKEY_TABLECODE from JE_CORE_TABLEKEY where TABLEKEY_RESOURCETABLE_ID=? and TABLEKEY_TYPE = 'Primary' AND TABLEKEY_ISCREATE = 1 order by SY_ORDERINDEX";
        COLUMNS = "select TABLECOLUMN_NAME,TABLECOLUMN_CODE,TABLECOLUMN_TYPE,TABLECOLUMN_LENGTH from JE_CORE_TABLECOLUMN where TABLECOLUMN_ISCREATE = 1 AND TABLECOLUMN_RESOURCETABLE_ID=?";
        FUNCTIONS = "select JE_CORE_FUNCINFO_ID,FUNCINFO_FUNCNAME,FUNCINFO_FUNCCODE,FUNCINFO_TABLENAME,FUNCINFO_COLUMNLAZY from JE_CORE_FUNCINFO where FUNCINFO_NODEINFOTYPE IN ('FUNCFIELD','FUNC') order by SY_ORDERINDEX";
        FUNCTIONS_IN = "select JE_CORE_FUNCINFO_ID,FUNCINFO_FUNCNAME,FUNCINFO_FUNCCODE,FUNCINFO_TABLENAME,FUNCINFO_COLUMNLAZY from JE_CORE_FUNCINFO where FUNCINFO_NODEINFOTYPE IN ('FUNCFIELD','FUNC') AND FUNCINFO_FUNCCODE = ? order by SY_ORDERINDEX";
        FUNCTION_LOAD_COLUMNS = "select RESOURCECOLUMN_CODE,RESOURCECOLUMN_NAME from JE_CORE_RESOURCECOLUMN where RESOURCECOLUMN_LAZYLOAD ='1' AND RESOURCECOLUMN_FUNCINFO_ID = ?";
    }

    /**
     * 数据源
     */
    protected DataSource dataSource;

    /**
     * JDBC模板
     */
    protected JdbcTemplate jdbcTemplate;

    public DefaultMetaDataParse() {
        jdbcTemplate = new JdbcTemplate();
    }

    @Override
    public List<Table> tables() {
        //查询表信息
        List<Map<String, Object>> list = list(TABLES);
        //组装Table实体
        ArrayList<Table> tables = new ArrayList<>();
        list.forEach(p -> tables.add(parseTable(p)));
        return tables;
    }

    @Override
    public List<Table> tables(List<String> tableCodes) {

        List<Map<String, Object>> list = list(TABLES_IN, tableCodes);
        //组装Table实体
        ArrayList<Table> tables = new ArrayList<>();
        list.forEach(p -> tables.add(parseTable(p)));
        return tables;
    }

    @Override
    public Table table(String tableCode) {
        List<Map<String, Object>> list = list(TABLES_IN, tableCode);
        //组装Table实体
        ArrayList<Table> tables = new ArrayList<>();
        list.forEach(p -> tables.add(parseTable(p)));
        return tables.isEmpty() ? null : tables.get(0);
    }

    @Override
    public List<Function> functions() {
        //查询功能信息
        List<Map<String, Object>> list = list(FUNCTIONS);
        //组装功能元数据对象
        ArrayList<Function> functions = new ArrayList<>();
        list.forEach(p -> functions.add(parseFunction(p)));
        return functions;
    }

    @Override
    public Function function(String functionCode) {
        List<Map<String, Object>> list = list(FUNCTIONS_IN, functionCode);
        return list.isEmpty() ? null : parseFunction(list.get(0));
    }

    /**
     * Map转换Table实体
     *
     * @param map 表信息Map
     * @return com.je.ibatis.extension.metadata.model.Table
     */
    protected Table parseTable(Map<String, Object> map) {

        //获取值
        Object tableId = map.get("JE_CORE_RESOURCETABLE_ID");
        Object tableName = map.get("RESOURCETABLE_TABLENAME");
        Object tableCode = map.get("RESOURCETABLE_TABLECODE");
        Object keyIgnoreCase = map.get("RESOURCETABLE_KEY_GENERATOR_TYPE");
        Object incrementerName = map.get("RESOURCETABLE_INCREMENTER_NAME");
        Object generatorSql = map.get("RESOURCETABLE_KEY_GENERATOR_SQL");
        //校验参数
        Assert.notNull(tableId, "field[JE_CORE_RESOURCETABLE_ID] is null!");
        Assert.notNull(tableCode, String.format("id[%s] field[RESOURCETABLE_TABLECODE] is null!", tableId));

        //初始化Table对象
        Table table = new Table();
        table.setCode(tableCode.toString());
        table.setName(tableName == null ? null : tableName.toString());

        //查询列信息
        List<Map<String, Object>> columnList = list(COLUMNS, tableId.toString());
        //添加列信息到Table实体
        columnList.forEach(c -> {
            Column column = parseColumn(c);
            if ("ID".equalsIgnoreCase(column.getType())) {
                table.setId(new Id(column.getCode(), IdType.getIdType(keyIgnoreCase), Objects.toString(incrementerName), Objects.toString(generatorSql)));
            }
            table.addColumn(column);
        });

        //查找主键
        if (table.getId() == null) {
            List<Map<String, Object>> keys = list(KEYS, tableId.toString());
            if (keys != null && !keys.isEmpty()) {
                if (keys.size() > 1) {
                    // TODO 提示当前包含多个主键字段
                }
                //获取主键字段名
                Object pkCode = keys.get(0).get("TABLEKEY_COLUMNCODE");
                table.setId(new Id(pkCode.toString(), IdType.getIdType(keyIgnoreCase), Objects.toString(incrementerName), Objects.toString(generatorSql)));
            }
        }
        return table;
    }

    /**
     * Map转换Column实体
     *
     * @param map 列信息Map
     * @return com.je.ibatis.extension.metadata.model.Column
     */
    protected Column parseColumn(Map<String, Object> map) {
        //获取值
        Object columnCode = map.get("TABLECOLUMN_CODE");
        Object columnName = map.get("TABLECOLUMN_NAME");
        Object columnType = map.get("TABLECOLUMN_TYPE");
        Object columnLength = map.get("TABLECOLUMN_LENGTH");
        Object isNull = map.get("TABLECOLUMN_ISNULL");

        //系统中的日期 其实是字符串
        if ("DATETIME".equalsIgnoreCase(String.valueOf(columnType)) || "DATE".equalsIgnoreCase(String.valueOf(columnType))) {
            columnType = "VARCHAR";
        }

        //如果类型为自定义，表示非系统支持类型，真实类型存在 TABLECOLUMN_LENGTH 中
        if ("CUSTOM".equalsIgnoreCase(String.valueOf(columnType)) && !StringUtils.isEmpty(columnLength)) {
            columnType = columnLength;
        }

        //校验参数
        Assert.notNull(columnCode, "field[TABLECOLUMN_CODE] is null!");
        Boolean isNullBoolean = false;
        if (String.valueOf(isNull) != null && String.valueOf(isNull).equals("1")) {
            isNullBoolean = true;
        }
        // TODO 完善列类型
        return new Column(String.valueOf(columnCode), String.valueOf(columnName), String.valueOf(columnType), isNullBoolean);
    }

    protected Function parseFunction(Map<String, Object> map) {

        //获取值
        Object id = map.get("JE_CORE_FUNCINFO_ID");
        Object code = map.get("FUNCINFO_FUNCCODE");
        Object name = map.get("FUNCINFO_FUNCNAME");
        Object tableCode = map.get("FUNCINFO_TABLENAME");
        Object lazy = map.get("FUNCINFO_COLUMNLAZY");
        //校验参数
        Assert.notNull(code, "field[JE_CORE_FUNCINFO_ID] is null!");
//        Assert.notNull(tableCode, String.format("id[%s] field[FUNCINFO_TABLENAME] is null!", tableCode));

        //初始化功能元数据对象
        List<String> loadColumnNames = new ArrayList<>();
        //查询列信息
        List<Map<String, Object>> columnList = list(FUNCTION_LOAD_COLUMNS, id.toString());
        //如果启用懒加载 设置查询列
        if ("1".equalsIgnoreCase(lazy.toString())) {
            columnList.forEach(c -> {
                loadColumnNames.add(c.get("RESOURCECOLUMN_CODE").toString());
            });
        }

        if (tableCode == null) {
            return new Function(code.toString(), null, loadColumnNames);
        }
        return new Function(code.toString(), tableCode.toString(), loadColumnNames);
    }

    /**
     * 执行查询
     *
     * @param sql    语句
     * @param params 预处理参数
     * @return
     */
    protected List<Map<String, Object>> list(String sql, Object... params) {
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params);
        if (list == null) {
            return new ArrayList<>();
        }
        return list;
    }

    @Override
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        jdbcTemplate.setDataSource(dataSource);
    }

    @Override
    public boolean formData(Table table, Map<String, Object> bean) {
        table.getColumnList().forEach(column -> {
            //判断类型
            String type = column.getType();
            //日期类型特殊处理
            if ("DATE".equalsIgnoreCase(type) || "TIMESTAMP".equalsIgnoreCase(type)) {
                Object value = bean.get(column.getCode());
                if (value instanceof String) {
                    Date date = DateUtils.parse(value);
                    if ("TIMESTAMP".equalsIgnoreCase(type)) {
                        bean.put(column.getCode(), new Timestamp(date.getTime()));
                    } else {
                        bean.put(column.getCode(), date);
                    }

                }
            }

            if (column.getNull() && null == bean.get(column.getCode())) {
                bean.put(column.getCode(), null);
            }

            if (bean.keySet().contains(column.getCode()) && Arrays.asList(NUMBER_FIELD_TYPE).contains(type) && (bean.get(column.getCode()) == null || bean.get(column.getCode()).equals(""))) {
                bean.put(column.getCode(), 0);
            }

        });
        return true;
    }
}